// filename ************** eFile.c *****************************
// Middle-level routines to implement a solid-state disk 
// Brian D. Bonnet 3/16/11

#include "efile.h"
#include "edisk.h"
#include <string.h>
#include <stdio.h>
#include "Interpreter.h"
#include "lm3s8962.h"

#define DRIVENUMBER		  0
#define DIRECTORY         0
#define NUMFILEBLOCKS     500
#define BLOCKSIZE         512
#define MAXFILESIZE       33291780
#define FREESPACENAME     496
#define FREESPACEPOINTER  506
#define NULL              0
#define NOOPEN            0
#define ROPEN             1
#define WOPEN             2
#define NEWFILE           3
#define NOTINIT           4
#define LF                10
#define CR                13

int NumFiles = 4;
WORD  FileStatus; // 0 = Nothing open, 1 = Currently Reading, 2 = Currently Writing, 3 = Creating New File, 4 = Not Initialized
WORD  DirIndex, FileLink, BlockIndex; 
DWORD FileLength;
BYTE  DirBlock[512], FileBlock[512];

//---------- eFile_Init-----------------
// Activate the file system, without formating
// Input: none
// Output: 0 if successful and 1 on failure (already initialized)
// since this program initializes the disk, it must run with 
//    the disk periodic task operating
int eFile_Init(void){ // initialize file system
  if(eDisk_Init(DRIVENUMBER)) // Initialization Failure
    return 1;

  NumFiles   = 0;
  FileStatus = 0;
  DirIndex   = 0;
  FileLink   = 0;
  BlockIndex = 0;
  FileLength = 0;

  return 0;
}
//---------- eFile_Format-----------------
// Erase all files, create blank directory, initialize free space manager
// Input: none
// Output: 0 if successful and 1 on failure (e.g., trouble writing to flash)
int eFile_Format(void){ // erase disk, add format
  BYTE tempBlock[512];
  WORD i;
  int j;

  // Clear blocks 1-n for files
  for(j=2; j<512; j++){
      tempBlock[j] = 0x00;
  }
  for(i=1; i < NUMFILEBLOCKS-1; i++){
	tempBlock[0] = i >> 8;
	tempBlock[1] = i;
	if(eDisk_WriteBlock(tempBlock, i-1)) // Return if write fails
	  return 1;
  }
  tempBlock[0] = 0x00;
  tempBlock[1] = 0x00;  // Last free block doesn't point to anyone
  if(eDisk_WriteBlock(tempBlock, i)) // Return if write fails
    return 1;
  
  // Set up Directory block & free space manager
  tempBlock[FREESPACENAME] = 0x2A; // Set '*' as file name for free space manager
  tempBlock[FREESPACEPOINTER] = 0x00;
  tempBlock[FREESPACEPOINTER+1] = 0x01; // Set free space pointer to first free block
  if(eDisk_WriteBlock(tempBlock, DIRECTORY))  // Return if write fails
    return 1;
  
  return 0;
}

//---------- eFile_Create-----------------
// Create a new, empty file with one allocated block
// Input: file name is an ASCII string up to seven characters 
// Output: 0 if successful and 1 on failure (e.g., trouble writing to flash)
int eFile_Create( char name[]){  // create new file, make it empty 
  int i,j,dirAddress;
  WORD fileLink, freeLink;

  if(name == NULL || name[0] == NULL) // Invalid name
    return 1;

  if(FileStatus) // A file is open
    return 1;

  if(eDisk_ReadBlock(DirBlock, DIRECTORY)) // Read failure
    return 1;

  // Get link to next free block of memory
  fileLink = (DirBlock[FREESPACEPOINTER] << 8) | DirBlock[FREESPACEPOINTER+1];

  if(fileLink == 0) // No open blocks exist
    return 1;

  // Find next open space in the directory
  i = 0;						  
  while(i < FREESPACENAME && DirBlock[i] != 0){
    i+= 16;
  }

  if(i == FREESPACENAME) // No space in directory
    return 1;

  dirAddress = i; // Save directory entry address
  j=0;
  // Copy File name into directory entry
  do{
	DirBlock[i++] = name[j];
  }while(name[j++] != NULL && j < 10);

  if(j == 10 & name[--j]){ // File name is greater than 10 chars (including NULL)
    do{
	  DirBlock[--i] = 0x00;
	}while(j-- > 0);
	return 1;
  }

  // Write address link
  i = dirAddress + 10;
  DirBlock[i++] = fileLink >> 8;
  DirBlock[i++] = fileLink;
  // Clear file size
  for(j=0; j<4; j++){
  	DirBlock[i++] = 0x00;
  } 

  // Read block where file will be save
  if(eDisk_ReadBlock(FileBlock, fileLink)) // Read failure
    return 1;

  freeLink = (FileBlock[0] << 8) | FileBlock[1]; // Get pointer to next free block
  
  // Clear file
  for(i=0; i < BLOCKSIZE; i++)
    FileBlock[i] = 0x00;

  if(eDisk_WriteBlock(FileBlock, fileLink)) // Write failure
    return 1;

  DirBlock[FREESPACEPOINTER] = freeLink >> 8;
  DirBlock[FREESPACEPOINTER+1] = freeLink;

  if(eDisk_WriteBlock(DirBlock, DIRECTORY)) // Write failure
    return 1;
  
  NumFiles++;
  return 0; // Success
}

//---------- eFile_WOpen-----------------
// Open the file, read into RAM last block
// Input: file name is a single ASCII letter
// Output: 0 if successful and 1 on failure (e.g., trouble writing to flash)
int eFile_WOpen(char name[]){      // open a file for writing 
  int j;
  WORD fLink;
  char fName[10];
  
  if(FileStatus) // Another file is open
    return 1;

  FileStatus = WOPEN;

  if(eDisk_ReadBlock(DirBlock, DIRECTORY)){ // Read failure
    FileStatus = NOOPEN;
	return 1;
  }


  DirIndex = 0;
  do{
    j=0;
    do{	// Copy file name
	  fName[j] = DirBlock[j+DirIndex];
	}while(DirBlock[DirIndex+(j++)] != NULL);
	if(strcmp(fName, name) == 0)
	  break;
	DirIndex += 16;
  }while(DirIndex < FREESPACENAME);

  if(DirIndex == FREESPACENAME){ // Name doesn't exist
    FileStatus = NOOPEN;
	return 1;
  }

  FileLength = (DirBlock[DirIndex+12] << 24) | (DirBlock[DirIndex+13] << 16) | (DirBlock[DirIndex+14] << 8) | (DirBlock[DirIndex+15]);

  // Find last block of file
  fLink = (DirBlock[DirIndex+10] <<  8) | DirBlock[DirIndex+11];
  while(fLink != 0){
    if(eDisk_ReadBlock(FileBlock, fLink)){ // Read failure
	  FileStatus = NOOPEN;
	  return 1;
	}
	FileLink = fLink;
	fLink = (FileBlock[0] << 8) | FileBlock[1];
  }

  BlockIndex = (FileBlock[2] << 8) | FileBlock[3];
  if(BlockIndex > 0){ // Back up one char to overwrite EOF character
    BlockIndex--;
	FileLength--;
  }

  return 0; 
}

//---------- eFile_Write-----------------
// save at end of the open file
// Input: data to be saved
// Output: 0 if successful and 1 on failure (e.g., trouble writing to flash)
int eFile_Write( char data){
  WORD freeLink;  

  if(FileStatus != WOPEN) // File write not currently open
    return 1;
  
  if(data == NULL) // EOF character added when file is closed
    return 0;

  if(BlockIndex < 508){
	FileBlock[BlockIndex + 4] = data;
    BlockIndex++; FileLength++;
    FileBlock[2] = BlockIndex >> 8;
    FileBlock[3] = BlockIndex;
  }  
  else{ // Block is full, append a new block to file and read it into the system
	freeLink = (DirBlock[FREESPACEPOINTER] << 8) | DirBlock[FREESPACEPOINTER+1];
	if(freeLink == 0) // No free blocks exist
	  return 1;
	FileBlock[0] = freeLink >> 8;
	FileBlock[1] = freeLink;
	
	if(eDisk_WriteBlock(FileBlock, FileLink)){ // Write failure
	  BlockIndex--;                     // Logically delete new data entry
	  FileBlock[2] = BlockIndex >> 8;
      FileBlock[3] = BlockIndex;  
	  return 1;
	}

	if(eDisk_ReadBlock(FileBlock, freeLink)) // Read error
	  return 1;

	// Update new file block
	FileLink = freeLink;
	freeLink = (FileBlock[0] << 8) | FileBlock[1];
	FileBlock[0] = 0x00;
    FileBlock[1] = 0x00;
	FileBlock[2] = 0x00;
	FileBlock[3] = 0x01;
	FileBlock[4] = data;
	BlockIndex = 1;
	FileLength++;
	
	// Update Directory
	DirBlock[FREESPACEPOINTER] = freeLink >> 8;
    DirBlock[FREESPACEPOINTER+1] = freeLink;
  }

  return 0;
}

//---------- eFile_Close-----------------
// Deactivate the file system
// Input: none
// Output: 0 if successful and 1 on failure (not currently open)
int eFile_Close(void){

} 


//---------- eFile_WClose-----------------
// close the file, left disk in a state power can be removed
// Input: none
// Output: 0 if successful and 1 on failure (e.g., trouble writing to flash)
int eFile_WClose(void){ // close the file for writing
  WORD freeLink;

  // Append EOF character
  if(BlockIndex < 508){
	FileBlock[BlockIndex + 4] = NULL;
    BlockIndex++; FileLength++;
    FileBlock[2] = BlockIndex >> 8;
    FileBlock[3] = BlockIndex;
  }  
  else{ // Block is full, append a new block to file and read it into the system
	freeLink = (DirBlock[FREESPACEPOINTER] << 8) | DirBlock[FREESPACEPOINTER+1];
	if(freeLink == 0){ // No free blocks exist, delete last char and put EOF
	  FileBlock[BLOCKSIZE-1] = NULL;
	}
	else{
	  FileBlock[0] = freeLink >> 8;
	  FileBlock[1] = freeLink;
	
	  if(eDisk_WriteBlock(FileBlock, FileLink)){ // Write failure
	    BlockIndex--;                     // Logically delete new data entry
	    FileBlock[2] = BlockIndex >> 8;
        FileBlock[3] = BlockIndex;  
	    return 1;
	  }

	  if(eDisk_ReadBlock(FileBlock, freeLink)) // Read error
	    return 1;

	  // Update new file block
	  FileLink = freeLink;
	  freeLink = (FileBlock[0] << 8) | FileBlock[1];
	  FileBlock[0] = 0x00;
      FileBlock[1] = 0x00;
	  FileBlock[2] = 0x00;
	  FileBlock[3] = 0x01;
	  FileBlock[4] = NULL;
	  BlockIndex = 1;
	  FileLength++;
	
	  // Update Directory
	  DirBlock[FREESPACEPOINTER] = freeLink >> 8;
      DirBlock[FREESPACEPOINTER+1] = freeLink;
	}
  }

  DirBlock[DirIndex + 12] = FileLength >> 24;
  DirBlock[DirIndex + 13] = FileLength >> 16;
  DirBlock[DirIndex + 14] = FileLength >> 8;
  DirBlock[DirIndex + 15] = FileLength;
  
  GPIO_PORTC_DATA_R |= 0x20;
  if(eDisk_WriteBlock(FileBlock, FileLink)) // Update File Block
    return 1;
  GPIO_PORTC_DATA_R &= ~0x20;
  if(eDisk_WriteBlock(DirBlock, DIRECTORY))	// Update Directory
    return 1;

  FileStatus = NOOPEN;
  return 0;
}

//---------- eFile_ROpen-----------------
// Open the file, read first block into RAM 
// Input: file name is a single ASCII letter
// Output: 0 if successful and 1 on failure (e.g., trouble read to flash)
int eFile_ROpen( char name[]){      // open a file for reading 
  int j;
  char fName[10];
  
  if(FileStatus) // Another file is open
    return 1;

  FileStatus = ROPEN;

  if(eDisk_ReadBlock(DirBlock, DIRECTORY)){ // Read failure
    FileStatus = NOOPEN;
	return 1;
  }


  DirIndex = 0;
  do{
    j=0;
    do{	// Copy file name
	  fName[j] = DirBlock[DirIndex+j];
	}while(DirBlock[DirIndex+(j++)] != NULL);
	if(strcmp(fName, name) == 0)
	  break;
	DirIndex += 16;
  }while(DirIndex < FREESPACENAME);

  if(DirIndex == FREESPACENAME){ // Name doesn't exist
    FileStatus = NOOPEN;
	return 1;
  }

  FileLink = (DirBlock[DirIndex+10] << 8) | DirBlock[DirIndex+11];

  if(eDisk_ReadBlock(FileBlock, FileLink))
    return 1;
  
  BlockIndex = 4; // Set Index to first BYTE in file

  return 0;
}
   
//---------- eFile_ReadNext-----------------
// retreive data from open file
// Input: none
// Output: return by reference data
//         0 if successful and 1 on failure (e.g., end of file)
int eFile_ReadNext( char *pt){       // get next byte 
  char data; 
  
  if(FileStatus != ROPEN)
    return 1;
  //if(BlockIndex == 500)
    //BlockIndex= BlockIndex;

  if(BlockIndex < 512){
    data = FileBlock[BlockIndex++];
	if(!data) // EOF
	  return 1;
	*pt = data;
	return 0;
  }
  else{
	FileLink = (FileBlock[0] << 8) | FileBlock[1];
	if(eDisk_ReadBlock(FileBlock, FileLink))
	  return 1;
	BlockIndex = 4;
	data = FileBlock[BlockIndex++];
	if(!data) // EOF
	  return 1;
	*pt = data;
	return 0;
  }
}
                              
//---------- eFile_RClose-----------------
// close the reading file
// Input: none
// Output: 0 if successful and 1 on failure (e.g., wasn't open)
int eFile_RClose(void){ // close the file for writing
  if(FileStatus != ROPEN)
    return 1;
  FileStatus = NOOPEN;
  BlockIndex = 0;
  return 0;
}

unsigned char DirHeader[] = "Current Directory:\n\r";
//---------- eFile_Directory-----------------
// Display the directory with filenames and sizes
// Input: pointer to a function that outputs ASCII characters to display
// Output: characters returned by reference
//         0 if successful and 1 on failure (e.g., trouble reading from flash)
int eFile_Directory(void(*fp)(unsigned char)){
  BYTE fSize[5], tempIndex;
  DWORD fLength;
  WORD dirIndex;
  int bufLen;

  if(FileStatus) // Disk is being modified
    return 1;

  FileStatus = 1;
  if(eDisk_ReadBlock(DirBlock, DIRECTORY))
    return 1;
  
  // Print Header
  tempIndex = 0;
  while(DirHeader[tempIndex]){
    fp(DirHeader[tempIndex++]);
  }

  // Print Current Directory
  dirIndex=0;
  while(dirIndex < FREESPACENAME){
	  if(DirBlock[dirIndex]){ // Does Entry Exist Here?
	    // Send File Name
	    tempIndex = dirIndex;
	    while(DirBlock[tempIndex]){
	      fp(DirBlock[tempIndex++]);
	    }
	    fp(CR);
	    fp(LF);
	    fLength = (DirBlock[dirIndex+12] << 24) | (DirBlock[DirIndex+13] << 16) | (DirBlock[DirIndex+14] << 8) | DirBlock[15];
	    sprintf(fSize,"%d", fLength);
	    tempIndex = 0;
	    // Send File Size
	    while(fSize[tempIndex]){
	      fp(fSize[tempIndex++]);
	    }
	    fp(CR);
	    fp(LF);
      }
	  
	  dirIndex += 16;
  }

  fp(CR);
  fp(LF);

  FileStatus = NOOPEN;
  return 0;
}   

//---------- eFile_Delete-----------------
// delete this file
// Input: file name is a single ASCII letter
// Output: 0 if successful and 1 on failure (e.g., trouble writing to flash)
int eFile_Delete( char name[]){  // remove this file 
  int j;
  char fName[10];
  WORD fLink, fLink2, freeLink;

  if(FileStatus) // Another file is open
    return 1;

  FileStatus = ROPEN;

  if(eDisk_ReadBlock(DirBlock, DIRECTORY)){ // Read failure
    FileStatus = NOOPEN;
	return 1;
  }


  DirIndex = 0;
  do{
    j=0;
    do{	// Copy file name
	  fName[j] = DirBlock[DirIndex+j];
	}while(DirBlock[DirIndex+(j++)] != NULL);
	if(strcmp(fName, name) == 0)
	  break;
	DirIndex += 16;
  }while(DirIndex < FREESPACENAME);

  if(DirIndex == FREESPACENAME){ // Name doesn't exist
    FileStatus = NOOPEN;
	return 1;
  }

  FileLink = (DirBlock[DirIndex+10] << 8) | DirBlock[DirIndex+11];
  freeLink = (DirBlock[FREESPACEPOINTER] << 8) | DirBlock[FREESPACEPOINTER+1];
  DirBlock[FREESPACEPOINTER] = FileLink >> 8;
  DirBlock[FREESPACEPOINTER+1] = FileLink;

  // Clear Dir entry
  for(j=0; j<16; j++){
	DirBlock[DirIndex+j] = 0x00;
  }

  // Logically clear all file blocks and link to free space
  fLink = FileLink;
  while(fLink){
    if(eDisk_ReadBlock(FileBlock, fLink))
	  return 1;
	FileBlock[2] = 0x00; // Block size = 0
	FileBlock[3] = 0x00;
	fLink2 = (FileBlock[0] << 8) | FileBlock[1]; // Get next block
	if(fLink2){ // Not last block in file
	  if(eDisk_WriteBlock(FileBlock,fLink))
	    return 1;
	}
	else{	   // Last block in file
	  if(freeLink){	// Other free blocks exist
	    FileBlock[0] = freeLink >> 8;
	    FileBlock[1] = freeLink;
	  }
	  if(eDisk_WriteBlock(FileBlock,fLink))
	    return 1;
	}
	fLink = fLink2;
  }
  
  if(eDisk_WriteBlock(DirBlock, DIRECTORY))
    return 1;

  FileStatus = NOOPEN;

  return 0;
}

//---------- eFile_RedirectToFile-----------------
// open a file for writing 
// Input: file name is a single ASCII letter
// stream printf data into file
// Output: 0 if successful and 1 on failure (e.g., trouble read/write to flash)

volatile int StreamToFile=0;                // 0=UART, 1=stream to file 
int eFile_RedirectToFile(char *name){ 
  eFile_Create(name);              // ignore error if file already exists 
  if(eFile_WOpen(name)) return 1;  // cannot open file 
  StreamToFile = 1; 
  return 0; 
}

//---------- eFile_EndRedirectToFile-----------------
// close the previously open file
// redirect printf data back to UART
// Output: 0 if successful and 1 on failure (e.g., wasn't open)
int eFile_EndRedirectToFile(void){ 
  StreamToFile = 0; 
  if(eFile_WClose()) return 1;    // cannot close file 
  return 0; 
}
 
int eFile_putc (int ch) {  
  if(StreamToFile){ 
    if(eFile_Write(ch)){          // close file on error 
       //eFile_EndRedirectToFile(); // cannot write to file 
       return 1;                  // failure 
  }    
    return 0; // success writing 
 }  
 // regular UART output 
  UART_OutChar(ch); 
  return 0;  
}

